package com.lemon.cloud.gateway.filter;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lemon.cloud.comm.constants.CacheConstants;
import com.lemon.cloud.comm.exception.ValidateCodeException;
import com.lemon.cloud.comm.model.ResultCode;
import com.lemon.cloud.comm.model.ResultMsg;
import com.lemon.cloud.gateway.config.GatewayConfigProperties;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import reactor.core.publisher.Mono;

import jakarta.validation.constraints.NotNull;
import java.nio.charset.StandardCharsets;
import java.util.List;

import static com.lemon.cloud.comm.constants.OAuth2Constant.PASSWORD_LOGIN;
import static com.lemon.cloud.comm.constants.OAuth2Constant.REFRESH_TOKEN;

/**
 * The type Validate code gateway filter.
 * 需要在网关中配置
 *
 * @date 验证码处理
 */
@Slf4j
@RequiredArgsConstructor
public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory<Object> {

    private final GatewayConfigProperties configProperties;

    private final ObjectMapper objectMapper;

    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            boolean isAuthToken = CharSequenceUtil.containsAnyIgnoreCase(request.getURI().getPath(), PASSWORD_LOGIN);

            // 不是登录请求，直接向下执行
            if (!isAuthToken) {
                return chain.filter(exchange);
            }

            // 刷新token，手机号登录（也可以这里进行校验） 直接向下执行
            String grantType = request.getQueryParams().getFirst("grant_type");
            if (StrUtil.equals(REFRESH_TOKEN, grantType)) {
                return chain.filter(exchange);
            }

            List<String> checkCodeGrantTypes = configProperties.getCheckCodeGrantTypes();
            boolean needCheck = CollectionUtil.isNotEmpty(checkCodeGrantTypes) && checkCodeGrantTypes.contains(grantType);
            try {
                // only oauth and the request not in ignore clients need check code.
                if (needCheck) {
                    checkCode(request);
                }
            } catch (Exception e) {
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED);
                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

                final String errMsg = e.getMessage();
                return response.writeWith(Mono.create(monoSink -> {
                    try {
                        byte[] bytes = objectMapper.writeValueAsBytes(ResultMsg.resultFail(ResultCode.NO_PERMISSION.getCode(), errMsg));
                        DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);

                        monoSink.success(dataBuffer);
                    } catch (JsonProcessingException jsonProcessingException) {
                        log.error("对象输出异常", jsonProcessingException);
                        monoSink.error(jsonProcessingException);
                    }
                }));
            }

            return chain.filter(exchange);
        };
    }

    @SneakyThrows
    private void checkCode(ServerHttpRequest request) {
        String code = request.getQueryParams().getFirst("code");

        if (CharSequenceUtil.isBlank(code)) {
            throw new ValidateCodeException("验证码不能为空");
        }

        String randomStr = request.getQueryParams().getFirst("randomStr");
        if (CharSequenceUtil.isBlank(randomStr)) {
            randomStr = request.getQueryParams().getFirst("mobile");
        }

        String key = CacheConstants.CODE_PREFIX + randomStr;

        Object codeObj = redisTemplate.opsForValue().get(key);

        redisTemplate.delete(key);
        if (ObjectUtil.isEmpty(codeObj)) {
            throw new ValidateCodeException("验证码已失效，请刷新");
        }

        if (!code.equals(codeObj)) {
            throw new ValidateCodeException("验证码不合法");
        }
    }


    @NotNull
    private static String[] splitClient(String header) {
        if (header == null || !header.startsWith("Basic")) {
            throw new RuntimeException("请求头中client信息为空");
        }
        byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
        byte[] decoded;
        try {
            decoded = Base64.decode(base64Token);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("Failed to decode basic authentication token");
        }

        String token = new String(decoded, StandardCharsets.UTF_8);

        int delim = token.indexOf(":");

        if (delim == -1) {
            throw new RuntimeException("Invalid basic authentication token");
        }
        return new String[]{token.substring(0, delim), token.substring(delim + 1)};
    }
}
